#pragma once

#include <iostream>
#include <sys/epoll.h>
#include <vector>
#include "Socket.hpp"
#include "Inet_Addr.hpp"

using namespace mySocket;

class epollServer
{
    const static int gnum = 64;

private:
    void addFd()
    {
        InetAddr clientAddr;
        int sockfd = _listenSock->socketAccept(&clientAddr)->getSockd();

        // 将新得到的IO套接字及其事件放入epoll模型
        epoll_event event;
        event.data.fd = sockfd;
        event.events = EPOLLIN;
        epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &event);

        LOG(DEBUG, "add: %d", event.data.fd);
    }

    void serviceIO(int pos)
    {
        // Echo
        int sockfd = ready_events[pos].data.fd;
        char buffer[1024] = {0};

        int n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // 该套接字的读事件已经就绪，无需等待
        if (n < 0)
        {
            LOG(FATA, "recv error");
            // 从epoll移除的文件描述符fd必须是合法的
            // 所以要先从epoll移除套接字，再关闭套接字
            epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);
        }
        else if (n == 0)
        {
            LOG(INFO, "client exited");
            epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);
        }
        else
        {
            // Echo
            std::string massage = "Echo# " + std::string(buffer);
            send(sockfd, massage.c_str(), massage.size(), 0);
        }
    }
    void serverHandler(int n)
    {
        // 遍历所有的已经就绪的fd及其事件
        for (int i = 0; i < n; i++)
        {
            // 读事件就绪
            if (ready_events[i].events & EPOLLIN)
            {
                // 如果收listen套接字
                if (ready_events[i].data.fd == _listenSock->getSockd())
                {
                    addFd();
                }
                else // 普通套接字
                {
                    serviceIO(i);
                }
            }
        }
    }

public:
    epollServer(uint16_t port)
        : _port(port),
          _listenSock(new tcpSocket)
    {
        _listenSock->tcpServerInit(InetAddr(_port));

        _epfd = epoll_create(128);
        if (_epfd < 0)
        {
            LOG(FATA, "epoll create failed");
            exit(7);
        }

        // 将listen套接字及其读事件放入epoll对象
        epoll_event event;
        event.events = EPOLLIN;
        event.data.fd = _listenSock->getSockd();
        epoll_ctl(_epfd, EPOLL_CTL_ADD, _listenSock->getSockd(), &event);

        LOG(DEBUG, "add listenSockfd: %d", event.data.fd);
    }

    void loop()
    {
        while (true)
        {
            int timeout = 1000; // 1000毫秒
            int n = epoll_wait(_epfd, ready_events, gnum, timeout);

            switch (n)
            {
            case 0:
                std::cout << "time out" << std::endl;
                break;
            case -1:
                std::cout << "epoll wait error" << std::endl;
                break;
            default:
                serverHandler(n);
                break;
            }
        }
    }

    ~epollServer()
    {
        _listenSock->socketClose();
        if (_epfd > 0)
            ::close(_epfd);
    }

private:
    uint16_t _port;
    socket_ptr _listenSock;
    int _epfd;

    epoll_event ready_events[gnum];
};